using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using Caliburn.Micro;
using MediaOwl.Common;
using MediaOwl.Common.Messages;
using MediaOwl.Common.ScreenManagement;
using MediaOwl.Netflix.Data;
using MediaOwl.Netflix.Model;
using MediaOwl.Netflix.NetflixServiceReference;

namespace MediaOwl.Netflix.ViewModels
{
    [Export, PartCreationPolicy(CreationPolicy.NonShared)]
    public class MoviePersonSingleViewModel : Workitem<NetflixViewModel>
    {
        #region Fields
        private readonly INetflixDataRepository repository;
        private readonly IEventAggregator eventAggregator;

        #endregion

        #region Constructor
        [ImportingConstructor]
        public MoviePersonSingleViewModel(INetflixDataRepository repository, IEventAggregator eventAggregator)
        {
            directorGenre = new BindableCollection<GenreExtended>();
            actorGenre = new BindableCollection<GenreExtended>();
            this.repository = repository;
            this.eventAggregator = eventAggregator;
        }

        #endregion

        #region Properties & Backingfields

        private Person currentPerson;
        public Person CurrentPerson
        {
            get { return currentPerson; }
            set
            {
                currentPerson = value;
                NotifyOfPropertyChange(() => CurrentPerson);
                NotifyOfPropertyChange(() => Awards);
                NotifyOfPropertyChange(() => MoviesDirected);
                NotifyOfPropertyChange(() => MoviesActed);
            }
        }

        private string personType;
        public string PersonType
        {
            get
            {
                return personType;
            }
            set
            {
                personType = value;
                NotifyOfPropertyChange(() => PersonType);
            }
        }

        private bool isActor;
        public bool IsActor
        {
            get { return isActor; }
            set
            {
                isActor = value;
                NotifyOfPropertyChange(() => IsActor);
            }
        }

        private bool isDirector;
        public bool IsDirector
        {
            get { return isDirector; }
            set
            {
                isDirector = value;
                NotifyOfPropertyChange(() => IsDirector);
            }
        }

        private double directorRating;
        public double DirectorRating
        {
            get
            {
                return directorRating;
            }
            set
            {
                directorRating = value;
                NotifyOfPropertyChange(() => DirectorRating);
            }
        }

        private double actorRating;
        public double ActorRating
        {
            get
            {
                return actorRating;
            }
            set
            {
                actorRating = value;
                NotifyOfPropertyChange(() => ActorRating);
            }
        }

        public BindableCollection<Title> MoviesDirected
        {
            get
            {
                if (CurrentPerson == null)
                    return null;
                return new BindableCollection<Title>(CurrentPerson.TitlesDirected.OrderByDescending(t => t.ReleaseYear));
            }
        }

        public BindableCollection<Title> MoviesActed
        {
            get
            {
                if (CurrentPerson == null)
                    return null;
                return new BindableCollection<Title>(CurrentPerson.TitlesActedIn.OrderByDescending(t => t.ReleaseYear));
            }
        }

        public BindableCollection<TitleAward> Awards
        {
            get
            {
                if (CurrentPerson == null)
                    return null;
                return new BindableCollection<TitleAward>(CurrentPerson.Awards.OrderBy(a => a.Type));
            }
        }

        private BindableCollection<GenreExtended> actorGenre;
        public BindableCollection<GenreExtended> ActorGenre
        {
            get { return actorGenre; }
            set
            {
                actorGenre = value;
                NotifyOfPropertyChange(() => ActorGenre);
            }
        }

        private BindableCollection<GenreExtended> directorGenre;
        public BindableCollection<GenreExtended> DirectorGenre
        {
            get { return directorGenre; }
            set
            {
                directorGenre = value;
                NotifyOfPropertyChange(() => DirectorGenre);
            }
        }

        #endregion

        #region Methods

        public void WithPerson(Person person)
        {
            if (person == null)
                return;
            DisplayName = person.Name;
            Run.Coroutine(FetchData(person.Id));
        }

        private IEnumerable<IResult> FetchData(int withId)
        {
            yield return Show.Busy(this);

            yield return repository.GetPerson(withId, x => CurrentPerson = x, x => eventAggregator.Publish(new ErrorMessage(x)));

            isActor = false;
            isDirector = false;
            PersonType = string.Empty;

            if (CurrentPerson != null)
            {
                if (CurrentPerson.TitlesActedIn.Count > 0)
                {
                    PersonType = "Actor";
                    IsActor = true;

                    double sum = CurrentPerson.TitlesActedIn.Where(title => title.AverageRating != null).Sum(title => title.AverageRating != null ? (double)title.AverageRating : 0);
                    ActorRating = sum / CurrentPerson.TitlesActedIn.Count(x => x.AverageRating != null) / 5;

                    foreach (var title in CurrentPerson.TitlesActedIn)
                    {
                        foreach (var genre in title.Genres)
                        {
                            if (genre == null) continue;
                            if (ActorGenre.Any(g => g.Name == genre.Name))
                            {
                                var genreExtended = ActorGenre.FirstOrDefault(g => g.Name == genre.Name);
                                if (genreExtended != null)
                                    genreExtended.Amount++;
                            }
                            else
                                ActorGenre.Add(new GenreExtended {Amount = 1, GenreBase = genre});
                        }
                    }

                    var topGenres = ActorGenre.OrderByDescending(g => g.Amount).Take(10);
                    ActorGenre = new BindableCollection<GenreExtended>(topGenres);
                }


                if (CurrentPerson.TitlesDirected.Count > 0)
                {
                    if (PersonType == "Actor")
                        PersonType += " and ";
                    PersonType += "Director";
                    IsDirector = true;
                    double sum = CurrentPerson.TitlesDirected.Where(title => title.AverageRating != null).Sum(title => title.AverageRating != null ? (double)title.AverageRating : 0);
                    DirectorRating = sum / CurrentPerson.TitlesDirected.Count(x => x.AverageRating != null) / 5;

                    foreach (var title in CurrentPerson.TitlesDirected)
                    {
                        foreach (var genre in title.Genres)
                        {
                            if (genre == null) continue;
                            // ReSharper disable AccessToModifiedClosure
                            if (DirectorGenre.Any(g => g.Name == genre.Name))

                            {
                                var genreExtended = DirectorGenre.FirstOrDefault(g => g.Name == genre.Name);
                                if (genreExtended != null)
                                    genreExtended.Amount++;
                            }
                            else
                                DirectorGenre.Add(new GenreExtended { Amount = 1, GenreBase = genre });
                            // ReSharper restore AccessToModifiedClosure
                        }
                    }
                    var topGenres = DirectorGenre.OrderByDescending(g => g.Amount).Take(10);
                    DirectorGenre = new BindableCollection<GenreExtended>(topGenres);
                }
            }

            yield return Show.NotBusy(this);
        }

        public IEnumerable<IResult> OpenTitle(object selectedItem)
        {
            if (selectedItem is Title)
            {
                yield return Show.Child<MovieTitleSingleViewModel>(Helper.GetScreenId(selectedItem))
                    .In(Parent)
                    .Configured(x => x.WithTitle((Title) selectedItem));
            }
        }

        public IEnumerable<IResult> OpenGenre(object selectedItem)
        {
            if (selectedItem is GenreExtended)
            {
                yield return Show.Child<MovieGenreSingleViewModel>(Helper.GetScreenId(selectedItem))
                    .In(Parent)
                    .Configured(x => x.WithGenre(((GenreExtended)selectedItem).GenreBase));
            }
        }

        #endregion
    }
}